package com.cf.lock.core;

import com.cf.spel.SpELService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.SpelCompilerMode;
import org.springframework.expression.spel.SpelParserConfiguration;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * game-server-LockService
 *
 * @author <a href="2428922347@qq.com">Zhu mingming</a>
 * @info
 * @since 2018/6/13 下午 07:21
 */
@Slf4j
public class LockService {


    @Autowired
    private ZLockProperties zLockProperties;

    @Autowired
    private SpELService spELService;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;


    public boolean lockWait(String key, long wait, long expire) {
        log.info("开始尝试加锁：【{}】，等待时常是【{}】", key, wait);
        Long totalWait = 0L;
        boolean isOk;
        long interval = 500;//时间间隔500ms
        while (true) {
            isOk = lockWithExpire(key, expire);
            if (isOk || totalWait > wait) {
                break;
            }
            try {
                Thread.sleep(interval);
                totalWait += interval;
            } catch (InterruptedException e) {
                break;
            }
        }
        return isOk;
    }

    private boolean lockWithExpire(String key, final long expire) {
        String value = UUID.randomUUID().toString();
        Boolean hasSet = stringRedisTemplate.opsForValue().setIfAbsent(key, value);
        if (hasSet) {
            stringRedisTemplate.expire(key, expire, TimeUnit.MILLISECONDS);
        }
        return hasSet;
    }


    public String getLockKey(Method method, Object[] ars) {
        ZLock lock = method.getAnnotation(ZLock.class);

        final String key = lock.key();

        //拼接lockKey前缀
        final StringBuilder keyBuilder = new StringBuilder(zLockProperties.getPrefixLockKey());
        if (StringUtils.isEmpty(key)) {
            //没有设置lockKey前缀，设置为方法名字
            keyBuilder.append(method.getName());
        } else {
            keyBuilder.append(key);
        }

        final String elValue = lock.value();
        if (!StringUtils.isEmpty(elValue)) {
            String value = spELService.getSpELValue(method, elValue, String.class, ars);
            keyBuilder.append(value);
        }

        return keyBuilder.toString();
    }


    public List<Map> getSystemLock() {
        Set<String> keys = this.stringRedisTemplate.keys(zLockProperties.getPrefixLockKey() + "*");
        List<String> keyList = Arrays.asList(keys.toArray(new String[]{}));
        List<String> valueList = this.stringRedisTemplate.opsForValue().multiGet(keyList);
        List<Map> locks = new LinkedList<Map>();
        for (int start = 0; start < keyList.size(); start++) {
            Map map = new HashMap();
            map.put("key", keyList.get(start));
            map.put("value", valueList.get(start));
            map.put("ttl", org.apache.commons.lang3.StringUtils.isEmpty(valueList.get(start)));
            locks.add(map);
        }
        return locks;
    }

    public void release(String key) {
        this.stringRedisTemplate.delete(key);
    }
}
